/*
 * 
 * Created on Sep 27, 2005
 */
package remato.client.controller;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

import remato.client.controller.exceptions.RematoClientException;
import remato.client.model.facade.IModelPersistable;
import remato.client.model.facade.ModelSystem;
import remato.client.view.facade.IExplorerViewPart;
import remato.client.view.facade.IExtendedPart;
import remato.client.view.session.LoginDialog;

import com.wgo.precise.client.common.session.ISessionHolder;
import com.wgo.precise.client.common.session.PreciseSession;
import com.wgo.precise.client.ui.controller.SessionHolderListener;

/**
 * The main plugin class to be used in the desktop.
 *
 * @author plhe - Petter L. H. Eide <petter@eide.biz>
 * @version $Id: RequirementPlugin.java,v 1.6 2006-02-06 09:29:08 petterei Exp $
 */
public class RequirementPlugin extends AbstractUIPlugin implements ISessionHolder<ClientSession>{
	
	/** The shared instance. */
	private static RequirementPlugin plugin;
	
	private ClientSession session;
	
	private Set<IExplorerViewPart> explorerViewParts = new HashSet<IExplorerViewPart>();
	
	private IExplorerViewPart currentExplorer;
	
	private Set<IExtendedPart> modelChangeListeners = Collections.synchronizedSet(new HashSet<IExtendedPart>());
	
	/** Listeners */
	private Set<IExtendedPart> modelUpdateListeners = new HashSet<IExtendedPart>();
	
	private EditorWindowManager windowManager = new PersistableEditorWindowManager();
	
	private Set<SessionHolderListener> sessionHolderListeners = new HashSet<SessionHolderListener>();
	
	public RequirementPlugin() {
		plugin = this;
	}
	
	@Override
	public void stop(BundleContext context) throws Exception {
//		windowManager.resetEditors();
		windowManager.closeEditors();
		super.stop(context);
		currentExplorer = null;
		plugin = null;
	}
	
	/**
	 * Returns the shared instance.
	 */
	public static RequirementPlugin getInstance() {
		return plugin;
	}
	
	/* (non-Javadoc)
	 * @see remato.client.controller.ISessionHolder#setSession(remato.client.controller.ClientSession)
	 */
	public boolean setSession(PreciseSession domainSession) {
		if (null != domainSession) {
			if (session != null ) {
				log(new ModelStatus(IStatus.WARNING, getClass().getSimpleName() + "Old session still active when new session were set. Disposing old session."));
				disposeSession();
			}
//			IRunnableWithProgress op = new ReflectionRunnableWithProgress("Loading initial data " + domainSession.getRemoteUrl(), ReflectionHelper.retrieveMethod(getClass().getSimpleName(), ClientSession.class, "ClientSession", domainSession.getClass().getInterfaces()[0]),ClientSession.class, domainSession);
//			IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
			try {
//				this.session = progressService.busyCursorWhile(op);
				this.session = new ClientSession(domainSession);
				initExplorerViewParts();
				windowManager.initEditors();
			} catch (Throwable t) {
				logAndDisplay(new ModelStatus(IStatus.ERROR, "An error occured while initialising the session.", t));
				return false;
			}
			notifySessionHolderListeners();
			return true;
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see remato.client.view.session.ISessionHolder#disposeSession()
	 */
	public boolean disposeSession() {
		return disposeSession(true, PropertyAccessor.getLocalString("logout.default_message"));
	}

	/* (non-Javadoc)
	 * @see remato.client.view.session.ISessionHolder#disposeSession(boolean)
	 */
	public boolean disposeSession(boolean saveChanges) {
		return disposeSession(true, null);
	}

	/* (non-Javadoc)
	 * @see remato.client.view.session.ISessionHolder#disposeSession(java.lang.String)
	 */
	public boolean disposeSession(String whySaveExplanation) {
		return disposeSession(true, whySaveExplanation);
	}

	private synchronized boolean disposeSession(boolean save, String whySaveExplanation) {
		// TODO: server logout
		if (session != null) {
			if (session.isDirty() && save) {
				if (null != whySaveExplanation) {
					MessageDialog dialog = new MessageDialog(getActiveShell(),
							"Save changes?", null, whySaveExplanation,
							MessageDialog.QUESTION, new String[] {
									IDialogConstants.OK_LABEL,
									IDialogConstants.NO_LABEL,
									IDialogConstants.CANCEL_LABEL }, 0);
					switch (dialog.open()) {
					case 0:
						getSession().getDomainSession().save();
						windowManager.flushNonPersistedItemsEditors();
						break;
					case 2:
						return false;
					}
				} else {
					getSession().getDomainSession().save();
					windowManager.flushNonPersistedItemsEditors();
				}
			}
		}
// 		windowManager.resetEditors();
		windowManager.closeEditors();
		if (null != session) {
			session.dispose();
		}
		session = null;
		for (IExplorerViewPart part : explorerViewParts) {
			part.reset();
		}
		notifySessionHolderListeners();
		return true;
	}
	
	/**
	 * @return Returns the session.
	 */
	public ClientSession getSession() {
		if (session == null) {
			try {
				throw new Exception("Who asks for the clientUserSession?");
			} catch (Exception e) {
				e.printStackTrace();
			}
			LoginDialog loginDialog = new LoginDialog(getWorkbench().getDisplay().getActiveShell(), this);
			// set some message..
			loginDialog.open();
		}
		return session;
	}
	
	/* (non-Javadoc)
	 * @see remato.client.view.session.ISessionHolder#isSessionSet()
	 */
	public boolean isSessionSet() {
		return null != session;
	}

	/**
	 * Eclipse log wrapper..
	 * @param message
	 */
	public static void log(String message) {
		IStatus status = new Status (IStatus.INFO, Registry.PLUGIN_ID, 0, message , null);
		log (status);
	}
	
	public static void log(RematoClientException e) {
		log (e.getStatus());
	}
	
	public static void log(Class clazz, String message) {
		log(clazz.getName() +": " + message);
	}
	
	public static void log(IStatus status) {
		getInstance().getLog().log( status );
	}
	
	public static void logAndDisplay(IStatus status){
		ErrorDialog.openError(getInstance().getActiveShell(), "ReMaTo message", "An error has occured.", status);
		RequirementPlugin.getInstance().getLog().log( status );
	}
	
	public static void log (Throwable e, String... strings) {
		String message = "";
		if (0 < strings.length) {
			message = strings[0];
		}
		log(new ModelStatus(IStatus.ERROR, message, e));
	}
	
	public boolean isLoggedIn() {
		boolean loggedIn = false;
		if (session != null) {
			loggedIn = true;
		}
		return loggedIn;
	}
	
	public void notifyModelUpdated(IModelPersistable item) {
		for (IExtendedPart listener : getInstance().modelUpdateListeners) {
			try {
				listener.onModelUpdate(item);
			} catch (SWTException e) {
				handlePartUpdateException(listener, e);
			}
		}
	}
	
	public void notifyModelChanged(IModelPersistable item) {
//		closeSpecializedEditors();
		for (IExtendedPart listener : modelChangeListeners) {
			try {
				listener.onModelChange(item);
			} catch (SWTException e) {
				handlePartUpdateException(listener, e);
			}
		}
	}
	public void notifyNewModelItemCreated(IModelPersistable item) {
//		closeSpecializedEditors();
		for (IExtendedPart listener : modelChangeListeners) {
			try {
				listener.onNewModelItemCreated(item);
			} catch (SWTException e) {
				handlePartUpdateException(listener, e);
			}
		}
	}
	public void notifyModelItemDeleted(IModelPersistable item){
		windowManager.closeEditorHierarchy(item);
		for (IExtendedPart listener : modelChangeListeners) {
			try {
				listener.onModelItemDeleted(item);
			} catch (SWTException e) {
				handlePartUpdateException(listener, e);
			}
		}
	}
	
	public void handlePartUpdateException(IExtendedPart part, SWTException e){
		log(e, "Error updating editor: " +part.getTitle());
	}
	
	private void initExplorerViewParts() {
		for (IExplorerViewPart explorer : explorerViewParts) {
			Collection<ModelSystem<?>> modelSystems = session.getWrapperFactory().getSystems(); 
			explorer.setContentInput(modelSystems.toArray(new ModelSystem[modelSystems.size()]));
		}
	}
	
	public static void addExplorerViewPart(IExplorerViewPart editorViewPart) {
		getInstance().explorerViewParts.add(editorViewPart);
	}
	
	
	public static void addModelUpdateListener(IExtendedPart listener){
		getInstance().modelUpdateListeners.add(listener);
	}
	
	public static void removeModelUpdateListener(IExtendedPart listener){
		getInstance().modelUpdateListeners.remove(listener);
	}
	
	public static void addModelChangedListener(IExtendedPart listener){
		getInstance().modelChangeListeners.add(listener);
	}
	
	public static void removeModelChangedListener(IExtendedPart listener){
		getInstance().modelChangeListeners.remove(listener);
	}
	
	public static void setCurrentExplorer(IExplorerViewPart currentExplorer) {
		getInstance().currentExplorer = currentExplorer;
	}
	
	public IExplorerViewPart getCurrentExplorer() {
		return currentExplorer;
	}
	
	public Shell getActiveShell() {
		return getWorkbench().getDisplay().getActiveShell();
	}

	public EditorWindowManager getEditorWindowManager() {
		return windowManager;
	}
	
	public void addSessionHolderListener(SessionHolderListener sessionHolderListener) {
		sessionHolderListeners.add(sessionHolderListener);
		sessionHolderListener.setSessionSet(session != null);
	}

	public void removeSessionHolderListener(SessionHolderListener sessionHolderListener) {
		sessionHolderListeners.remove(sessionHolderListener);
	}

	private void notifySessionHolderListeners() {
		for (SessionHolderListener sessionHolderListener : sessionHolderListeners) {
			sessionHolderListener.setSessionSet( session!= null ) ;
		}
	}
}
